001 /** 002 * Created by IntelliJ IDEA. 003 * User: Wei Wang 004 * Date: Nov 27, 2002 005 * Time: 3:50:11 PM 006 */ 007 008 package EVolve.visualization; 009 010 import EVolve.data.*; 011 import EVolve.Scene; 012 import EVolve.visualization.VizFactory.VisualizationFactory; 013 014 import javax.swing.*; 015 import javax.imageio.*; 016 import java.awt.event.*; 017 import java.awt.image.BufferedImage; 018 import java.awt.*; 019 import java.util.*; 020 import java.io.*; 021 022 public abstract class Visualization implements Cloneable{ 023 public static final int SELECT_TIME_FRAME = 0x0001; 024 public static final int SELECT_OCCURRED_ENTITIES = 0x0010; 025 public static final int SELECT_ALL_ENTITIES = 0x0100; 026 public static final int SELECT_X_AXIS = 0x1000; 027 public static final int SELECT_Y_AXIS = 0x0100; 028 public static final int SELECT_ALL_X_AXIS = 0x0010; 029 public static final int SELECT_ALL_Y_AXIS = 0x0001; 030 031 032 protected String name; // name of the visualization 033 protected EVolve.Window window; // window where the visualization is displayed 034 035 protected VisualizationDefinition definition; // definition of the visualization 036 protected ElementDefinition subjectDefinition; // definition of the subject 037 038 protected Dimension[] dimension; // dimension of the visualization 039 protected Component panel; // panel to draw the visualization 040 041 protected JDialog dialog; // configuration dialog 042 protected JPanel panelConfiguration; // configuration panel 043 protected JComboBox comboSubject; // subject selection 044 protected JComboBox[] comboDimension; // dimension selection 045 protected JTextField textName; // name input 046 047 protected JPopupMenu popup; // popup menu 048 private JMenu menuSort; 049 private JMenu[] menuDimension; 050 protected JMenuItem[][] itemSort; 051 private JMenuItem itemConfigure,itemRename; 052 private JMenuItem itemSelect,itemSave; 053 private JMenuItem itemClone; 054 private ReferenceDimension[] referenceDimension; 055 private int linkageId; 056 private VisualizationFactory factory = null; 057 private static int vizIDPool = 0; 058 protected int dataSourceId; 059 060 protected ElementDefinition[] elementDefinition; // definition of elements that can be used as subject 061 protected DataFilter[][][] dataFilter; // data filters that can be used by the dimensions 062 protected int mouseX, mouseY; 063 064 protected boolean freezed; 065 066 public Visualization() { 067 dimension = createDimension(); 068 panel = createPanel(); 069 panelConfiguration = null; 070 dialog = null; 071 linkageId = vizIDPool++; 072 addPopupTrigger(panel); 073 freezed = false; 074 } 075 076 public String getName() { 077 return name; 078 } 079 080 /** 081 * Sets the name of the visualization. 082 * 083 * @param name name of the visualization 084 */ 085 public void setName(String name) { 086 this.name = name; 087 } 088 089 /** 090 * Sets the window of the visualization. 091 * 092 * @param window window of the visualization 093 */ 094 public void setWindow(EVolve.Window window) { 095 this.window = window; 096 } 097 098 /** 099 * Gets the panel of the visualization. 100 * 101 * @return panel of the visualization 102 */ 103 public Component getPanel() { 104 return panel; 105 } 106 107 /** 108 * Gets the additional configuration panel. 109 * 110 * @return the additional configuration panel, null if not necessary 111 */ 112 protected JPanel createConfigurationPanel() { 113 return null; // by default, return null, each visualization can override this 114 } 115 116 protected void createMenu() { 117 popup = new JPopupMenu(); 118 119 itemConfigure = new JMenuItem("Configure..."); 120 itemConfigure.setMnemonic(KeyEvent.VK_C); 121 itemConfigure.addActionListener(new ActionListener() { 122 public void actionPerformed(ActionEvent e) { 123 configure(); 124 } 125 }); 126 popup.add(itemConfigure); 127 128 itemRename = new JMenuItem("Rename..."); 129 itemRename.setMnemonic(KeyEvent.VK_R); 130 itemRename.addActionListener(new ActionListener() { 131 public void actionPerformed(ActionEvent e) { 132 String newName = JOptionPane.showInputDialog(Scene.getFrame(),"Enter a new name:",name); 133 if (newName != null) { 134 name = newName; 135 window.setTitle(name); 136 } 137 } 138 }); 139 popup.add(itemRename); 140 141 itemSelect = new JMenuItem("Make Selection"); 142 itemSelect.setMnemonic(KeyEvent.VK_M); 143 itemSelect.addActionListener(new ActionListener() { 144 public void actionPerformed(ActionEvent e) { 145 makeSelection(); 146 } 147 }); 148 popup.add(itemSelect); 149 150 menuSort = new JMenu("Sort"); 151 menuSort.setMnemonic(KeyEvent.VK_S); 152 menuSort.setEnabled(false); 153 popup.add(menuSort); 154 155 ArrayList tempList = new ArrayList(); 156 for (int i = 0; i < dimension.length; i++) { 157 if (dimension[i] instanceof ReferenceDimension) { 158 tempList.add(new Integer(i)); 159 } 160 } 161 162 referenceDimension = new ReferenceDimension[tempList.size()]; 163 menuDimension = new JMenu[tempList.size()]; 164 itemSort = new JMenuItem[tempList.size()][]; 165 for (int i = 0; i < tempList.size(); i++) { 166 int j = ((Integer)(tempList.get(i))).intValue(); 167 referenceDimension[i] = (ReferenceDimension)(dimension[j]); 168 menuDimension[i] = new JMenu(definition.getDimensionDefinition()[j].getName()); 169 menuSort.add(menuDimension[i]); 170 } 171 172 itemClone = new JMenuItem("Clone"); 173 itemClone.setMnemonic(KeyEvent.VK_C); 174 itemClone.addActionListener(new ActionListener() { 175 public void actionPerformed(ActionEvent e) { 176 Scene.getVisualizationManager().cloneVisualization(); 177 } 178 }); 179 popup.add(itemClone); 180 181 itemSave = new JMenuItem("Save..."); 182 itemSave.setMnemonic(KeyEvent.VK_V); 183 itemSave.addActionListener(new ActionListener() { 184 public void actionPerformed(ActionEvent e) { 185 save(); 186 } 187 }); 188 popup.add(itemSave); 189 } 190 191 private void showPopup(MouseEvent e) { 192 mouseX = e.getX(); 193 mouseY = e.getY(); 194 Rectangle rect = e.getComponent().getBounds(); 195 Rectangle rect2 = popup.getBounds(); 196 int posX = mouseX, posY = mouseY; 197 if (mouseY+rect2.height > rect.height) 198 posY = posY - rect2.height; 199 if (mouseX+rect2.width>rect.width) 200 posX = posX - rect2.width; 201 popup.show(e.getComponent(), posX, posY); 202 } 203 204 /** 205 * Sets the definition of the visualization, called by visualization manager after the visualization is created. 206 * 207 * @param definition definition of the visualization 208 */ 209 public void setDefinition(VisualizationDefinition definition) { 210 panelConfiguration = createConfigurationPanel(); 211 212 this.definition = definition; 213 214 elementDefinition = Scene.getDataManager().getElementDefinition(definition); 215 216 dataFilter = new DataFilter[elementDefinition.length][definition.getDimensionDefinition().length][]; 217 for (int i = 0; i < dataFilter.length; i++) { 218 for (int j = 0; j < dataFilter[i].length; j++) { 219 dataFilter[i][j] = Scene.getDataManager().getDataFilter(elementDefinition[i],definition.getDimensionDefinition()[j].getProperty()); 220 for (int k = 0; k < dataFilter[i][j].length; k++) { 221 assert (((dataFilter[i][j][k].getTargetType() != -1) && (dimension[j] instanceof ReferenceDimension)) || ((dataFilter[i][j][k].getTargetType() == -1) && (dimension[j] instanceof ValueDimension))) : "Incompatible dimension type."; 222 } 223 } 224 } 225 226 subjectDefinition = elementDefinition[0]; 227 for (int i = 0; i < dimension.length; i++) { 228 dimension[i].setDataFilter(dataFilter[0][i][0]); 229 } 230 231 updateConfiguration(); 232 233 createMenu(); 234 } 235 236 /** 237 * Gets the definition of the subject. 238 * 239 * @return definition of the subject 240 */ 241 public ElementDefinition getSubjectDefinition() { 242 return subjectDefinition; 243 } 244 245 /** 246 * Configures the visualization. 247 */ 248 public void configure() { 249 if (dialog == null) { 250 createDialog(); 251 } 252 dialog.pack(); 253 Scene.getUIManager().showDialog(dialog, dialog.getWidth(), dialog.getHeight()); 254 } 255 256 /** 257 * Save visualizations as disk file 258 */ 259 public void save() { 260 Component target = panel; 261 if (target instanceof JScrollPane) 262 target = ((JScrollPane)panel).getViewport().getView(); 263 264 BufferedImage image = new BufferedImage(target.getWidth(), target.getHeight(), BufferedImage.TYPE_INT_RGB); 265 target.paint(image.getGraphics()); 266 ImageWriter writer = (ImageWriter)(ImageIO.getImageWritersByFormatName("png").next()); 267 268 JFileChooser fc = new JFileChooser(Scene.getUIManager().getLastResultDir()); 269 fc.setFileFilter(new javax.swing.filechooser.FileFilter() { 270 public String getDescription() { 271 return "PNG Files"; 272 } 273 274 public boolean accept(File f) { 275 return f.getName().substring(f.getName().lastIndexOf('.') + 1).toLowerCase().equals("png"); 276 } 277 }); 278 279 if (fc.showSaveDialog(Scene.getFrame()) == JFileChooser.APPROVE_OPTION) { 280 File file = fc.getSelectedFile(); 281 Scene.getUIManager().setLastResultDir(file.getPath()); 282 try { 283 writer.setOutput(ImageIO.createImageOutputStream(file)); 284 writer.write(image); 285 } catch (IOException e) {} 286 } 287 } 288 289 /** 290 * Creates the configuration dialog. 291 */ 292 protected void createDialog() { 293 dialog = new JDialog(Scene.getFrame(), "Configure", true); 294 295 JPanel panelTitle = new JPanel(new FlowLayout()); 296 dialog.getContentPane().add(panelTitle, BorderLayout.NORTH); 297 298 panelTitle.add(new JLabel("Title: ")); 299 300 textName = new JTextField(name, 12); 301 panelTitle.add(textName); 302 303 JPanel panelMain = new JPanel(new BorderLayout()); 304 panelMain.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Choose subject & dimensions")); 305 dialog.getContentPane().add(panelMain, BorderLayout.CENTER); 306 307 JPanel panelName = new JPanel(new GridLayout(definition.getDimensionDefinition().length + 1, 1, 5, 5)); 308 panelMain.add(panelName, BorderLayout.WEST); 309 310 panelName.add(new JLabel(" Subject: ")); 311 for (int i = 0; i < definition.getDimensionDefinition().length; i++) { 312 panelName.add(new JLabel(" " + definition.getDimensionDefinition()[i].getName() + ": ")); 313 } 314 315 JPanel panelCombo = new JPanel(new GridLayout(definition.getDimensionDefinition().length + 1, 1, 5, 5)); 316 panelMain.add(panelCombo, BorderLayout.CENTER); 317 318 comboSubject = new JComboBox(); 319 for (int i = 0; i < elementDefinition.length; i++) { 320 comboSubject.addItem(elementDefinition[i].getName()); 321 } 322 comboSubject.addActionListener(new ActionListener() { 323 public void actionPerformed(ActionEvent e) { 324 updateComboSubject(); 325 } 326 }); 327 panelCombo.add(comboSubject); 328 329 comboDimension = new JComboBox[definition.getDimensionDefinition().length]; 330 for (int i = 0; i < comboDimension.length; i++) { 331 comboDimension[i] = new JComboBox(); 332 comboDimension[i].addActionListener(new ActionListener() { 333 public void actionPerformed(ActionEvent e) { 334 updateComboDimension(); 335 } 336 }); 337 panelCombo.add(comboDimension[i]); 338 } 339 340 updateComboSubject(); 341 342 if (panelConfiguration != null) { 343 panelMain.add(panelConfiguration, BorderLayout.SOUTH); 344 } 345 346 JPanel panelButton = new JPanel(new FlowLayout()); 347 dialog.getContentPane().add(panelButton, BorderLayout.SOUTH); 348 349 JButton buttonApply = new JButton("Apply"); 350 buttonApply.addActionListener(new ActionListener() { 351 public void actionPerformed(ActionEvent e) { 352 dialogApply(); 353 } 354 }); 355 panelButton.add(buttonApply); 356 357 JButton buttonCancel = new JButton("Cancel"); 358 buttonCancel.addActionListener(new ActionListener() { 359 public void actionPerformed(ActionEvent e) { 360 dialogCancel(); 361 } 362 }); 363 panelButton.add(buttonCancel); 364 365 //************* 366 for (int i=0; i<comboSubject.getItemCount(); i++) { 367 if ((comboSubject.getItemAt(i)).equals(subjectDefinition.getName())) { 368 comboSubject.setSelectedIndex(i); 369 break; 370 } 371 } 372 for (int i=0;i<comboDimension.length;i++) { 373 for (int j =0 ; j<comboDimension[i].getItemCount();j++) { 374 if (comboDimension[i].getItemAt(j).equals(dimension[i].getDataFilter().getName())) { 375 comboDimension[i].setSelectedIndex(j); 376 break; 377 } 378 } 379 } 380 } 381 382 /** 383 * Updates the subject combo-box. 384 */ 385 protected void updateComboSubject() { 386 comboSubject.setToolTipText(elementDefinition[comboSubject.getSelectedIndex()].getDescription()); 387 for (int i = 0; i < comboDimension.length; i++) { 388 comboDimension[i].removeAllItems(); 389 for (int j = 0; j < dataFilter[comboSubject.getSelectedIndex()][i].length; j++) { 390 comboDimension[i].addItem(dataFilter[comboSubject.getSelectedIndex()][i][j].getName()); 391 } 392 } 393 updateComboDimension(); 394 395 } 396 397 /** 398 * Updates the dimension combo-boxes. 399 */ 400 protected void updateComboDimension() { 401 for (int i = 0; i < comboDimension.length; i++) { 402 if (comboDimension[i].getSelectedIndex() != -1) { 403 comboDimension[i].setToolTipText(dataFilter[comboSubject.getSelectedIndex()][i][comboDimension[i].getSelectedIndex()].getDescription()); 404 } 405 } 406 } 407 408 /** 409 * Button "Apply" is clicked. 410 */ 411 protected void dialogApply() { 412 dialog.setVisible(false); 413 subjectDefinition = elementDefinition[comboSubject.getSelectedIndex()]; 414 name = textName.getText(); 415 if (name.indexOf(subjectDefinition.getName()) == -1) 416 window.setTitle(name + " - " + subjectDefinition.getName()); 417 else 418 window.setTitle(name); 419 reset(); 420 updateConfiguration(); 421 } 422 423 /** 424 * Button "Cancel" is clicked. 425 */ 426 protected void dialogCancel() { 427 dialog.setVisible(false); 428 } 429 430 public void updateMenu() { 431 for (int i = 0; i < referenceDimension.length; i++) { 432 ArrayList comparatorList = referenceDimension[i].getComparator(); 433 itemSort[i] = new JMenuItem[comparatorList.size()]; 434 menuDimension[i].removeAll(); 435 for (int j = 0; j < itemSort[i].length; j++) { 436 itemSort[i][j] = new JMenuItem(((EntityComparator)(comparatorList.get(j))).getName()); 437 itemSort[i][j].addActionListener(new ActionListener() { 438 public void actionPerformed(ActionEvent e) { 439 selectComparator(e); 440 } 441 }); 442 menuDimension[i].add(itemSort[i][j]); 443 } 444 } 445 446 ArrayList menuOptional = createOptionalMenu(); 447 if (menuOptional == null ) return; 448 for (int i=0; i<menuOptional.size(); i++) 449 popup.add((JMenuItem)menuOptional.get(i)); 450 } 451 452 protected ArrayList createOptionalMenu() { 453 return null; 454 } 455 456 public void selectComparator(ActionEvent e) { 457 for (int i = 0; i < itemSort.length; i++) { 458 for (int j = 0; j < itemSort[i].length; j++) { 459 if (itemSort[i][j] == e.getSource()) { 460 referenceDimension[i].selectComparator(j); 461 sort(); 462 return; 463 } 464 } 465 } 466 } 467 468 protected void addPopupTrigger(Component component) { 469 Component target = component; 470 if (target instanceof JScrollPane) 471 target = ((JScrollPane)target).getViewport().getView(); 472 473 target.addMouseListener(new MouseAdapter() { 474 public void mouseReleased(MouseEvent e) { 475 if (e.isPopupTrigger()) { 476 showPopup(e); 477 } 478 } 479 480 public void mousePressed(MouseEvent e) { 481 if (e.isPopupTrigger()) { 482 showPopup(e); 483 } 484 } 485 }); 486 } 487 488 public void enableSortMenu() { 489 menuSort.setEnabled(true); 490 } 491 492 /** 493 * Creates the dimensions. 494 * 495 * @return the dimensions 496 */ 497 public abstract Dimension[] createDimension(); 498 499 /** 500 * Creates available selection menu items. 501 * 502 * @return the array of menu items 503 */ 504 public abstract JMenuItem[] createSelectionMenuItem(); 505 506 /** 507 * Creates the panel. 508 * 509 * @return the panel 510 */ 511 protected abstract Component createPanel(); 512 513 /** 514 * Updates the configuration. 515 */ 516 protected abstract void updateConfiguration(); 517 518 /** 519 * Gets ready for receiving data. 520 */ 521 public abstract void preVisualize(); 522 523 /** 524 * Receives data. 525 * 526 * @param data the data 527 */ 528 protected abstract void receiveElement(Element element); 529 530 /** 531 * Visualizes after receiving data. 532 */ 533 public abstract void visualize(); 534 535 /** 536 * sorting the visualization with selected comparator 537 */ 538 public abstract void sort(); 539 540 /** 541 * according to user selectiong, get a data subset 542 */ 543 public abstract void makeSelection(); 544 545 /** 546 * 547 * @return a Hash map contains all current visualization's configure 548 */ 549 public HashMap getCurrentConfigure() { 550 HashMap configure = new HashMap(); 551 552 configure.put("Factory",getFactory()); 553 configure.put("Subject",subjectDefinition); 554 configure.put("Name",name); 555 configure.put("Interval", new Integer(-1)); 556 configure.put("Options", ";not using any options"); 557 558 return configure; 559 } 560 561 /** 562 * following are all interfaces needed for predefined viz 563 */ 564 public void autoUpdateConfiguration(HashMap config) { 565 566 int selectedSubjectId = -1; 567 Dimension [] predefinedDimension; 568 569 for (int i=0; i< elementDefinition.length; i++) { 570 if (elementDefinition[i].getName().equals(((ElementDefinition)config.get("Subject")).getName())) { 571 subjectDefinition = elementDefinition[i]; 572 selectedSubjectId = i; 573 break; 574 } 575 } 576 577 predefinedDimension = (Dimension[])config.get("Dimension"); 578 for (int i=0; i<dimension.length; i++) { 579 for (int j=0; j< dataFilter[selectedSubjectId][i].length;j++) { 580 if (dataFilter[selectedSubjectId][i][j].getName().equals(predefinedDimension[i].getDataFilter().getName())) { 581 dimension[i].setDataFilter(dataFilter[selectedSubjectId][i][j]); 582 break; 583 } 584 } 585 } 586 587 updateConfiguration(); 588 window.setTitle(name); 589 menuSort.setEnabled(false); 590 } 591 592 public EVolve.Window getWindow() { 593 return window; 594 } 595 596 public void setFactory(VisualizationFactory factory) { 597 this.factory = factory; 598 } 599 600 public VisualizationFactory getFactory() { 601 return factory; 602 } 603 604 public void autoSave(String path, String dataFn) { 605 Component target = panel; 606 if (target instanceof JScrollPane) 607 target = ((JScrollPane)panel).getViewport().getView(); 608 609 BufferedImage image = new BufferedImage(target.getWidth(), target.getHeight(), BufferedImage.TYPE_INT_RGB); 610 target.paint(image.getGraphics()); 611 ImageWriter writer = (ImageWriter)(ImageIO.getImageWritersByFormatName("png").next()); 612 613 try { 614 RandomAccessFile file = new RandomAccessFile((path+File.separator+ 615 dataFn.substring(dataFn.lastIndexOf(File.separator)+1,dataFn.length()-4)+".png"),"rw"); 616 writer.setOutput(ImageIO.createImageOutputStream(file)); 617 writer.write(image); 618 } catch (IOException e) { 619 System.out.println("writing file error."); 620 } 621 622 } 623 624 /** 625 * following methods are used in unify/overlap viz 626 */ 627 public abstract ReferenceDimension getLinkableDimension(int dim); 628 629 public int getVisualizationID() { 630 return linkageId; 631 } 632 633 public Dimension[] getDimension() { 634 return dimension; 635 } 636 637 public VisualizationDefinition getDefinition() { 638 return definition; 639 } 640 641 public long getxMax() { 642 return -1; 643 } 644 645 public AutoImage getImage() { 646 return null; 647 } 648 649 public void setImage(AutoImage image) { 650 return; 651 } 652 653 public void cleanup() { 654 dimension = null; 655 comboDimension = null; 656 menuDimension = null; 657 itemSort = null; 658 referenceDimension = null; 659 elementDefinition = null; 660 dataFilter = null; 661 } 662 663 protected int switchOption(boolean turn_on, int option, int add_on_option) { 664 int returnVal; 665 if (turn_on) 666 returnVal = option | add_on_option; 667 else 668 returnVal = option & ~add_on_option; 669 return returnVal; 670 } 671 672 public void setDataSourceId(int dataSourceId) { 673 this.dataSourceId = dataSourceId; 674 } 675 676 public int getDataSourceId() { 677 return dataSourceId; 678 } 679 680 protected void reset() { 681 for (int i = 0; i < dimension.length; i++) { 682 int selected = comboDimension[i].getSelectedIndex(); 683 if (selected < dataFilter[comboSubject.getSelectedIndex()][i].length) 684 dimension[i].setDataFilter(dataFilter[comboSubject.getSelectedIndex()][i][selected]); 685 if (dimension[i] instanceof ReferenceDimension) ((ReferenceDimension)dimension[i]).clearEntityMap(); 686 } 687 menuSort.setEnabled(false); 688 } 689 690 public boolean isFreezed() { 691 return freezed; 692 } 693 694 public Object clone() { 695 Visualization o = null; 696 697 try { 698 o = (Visualization)super.clone(); 699 } catch (CloneNotSupportedException e) { 700 e.printStackTrace(); 701 return null; 702 } 703 704 o.name = name + "- cloned"; 705 o.definition = (VisualizationDefinition)definition.clone(); 706 o.subjectDefinition = (ElementDefinition) subjectDefinition.clone(); 707 o.dimension = new Dimension[dimension.length]; 708 o.factory = (VisualizationFactory)factory.clone(); 709 o.elementDefinition = new ElementDefinition[elementDefinition.length]; 710 for (int i=0; i<elementDefinition.length; i++) 711 o.elementDefinition[i] = (ElementDefinition)elementDefinition[i].clone(); 712 o.dataFilter = new DataFilter[dataFilter.length][][]; 713 o.linkageId = vizIDPool++; 714 for (int i=0; i<dataFilter.length; i++) { 715 o.dataFilter[i] = new DataFilter[dataFilter[i].length][]; 716 for (int j=0; j<dataFilter[i].length; j++) { 717 o.dataFilter[i][j] = new DataFilter[dataFilter[i][j].length]; 718 for (int k=0; k<dataFilter[i][j].length; k++) 719 o.dataFilter[i][j][k] = (DataFilter)dataFilter[i][j][k].clone(); 720 } 721 } 722 return o; 723 } 724 }